#ifndef __LOGGING_H_
#define __LOGGING_H_

#pragma once

#include "Win32header.h"
#include "ThreadSafeHelper.h"
#include <string>
#include <vector>
#include <fstream>

template < class T >
class MemoryPool {
public:
	// If the freeList is empty, expand it by this amount.
	enum { EXPANSION_SIZE = 32};

	MemoryPool (size_t size = EXPANSION_SIZE);
	~MemoryPool ();

	// Allocate a T element from the free list.
	inline void* alloc (size_t size);
	// Return a T element to the free list.
	inline void free (void *someElement);

private:
	// Add free elements to the free list
	void expandTheFreeList(int howMany = EXPANSION_SIZE);
	// next element on the free list.

private:
	union Block{
		union Block* next;
		char data[1]; // The data sees this.
	};

	Block* free_block;

	CriticalSection	_cs;
};

template < class T >
MemoryPool < T > :: MemoryPool (size_t size)
{
	expandTheFreeList(size);
}

template < class T >
MemoryPool < T > :: ~MemoryPool ()
{
	Block *nextPtr = free_block;
	for (nextPtr = free_block; nextPtr != NULL; nextPtr = free_block) 
	{
		free_block = free_block->next;		
		delete [] ((char*)nextPtr);
	}
}

template < class T >
inline
void* MemoryPool < T > :: alloc (size_t)
{
	ScopedLock guard(_cs);

	if (!free_block) {
		expandTheFreeList();
	}
	Block *head = free_block;
	free_block = head->next;
	return head;
}

template < class T >
inline
void MemoryPool< T > :: free (void *doomed)
{
	ScopedLock guard(_cs);

	Block *head = (Block *)doomed;//static_cast <Block *> doomed;
	head->next = free_block;
	free_block = head;
}

template < class T >
void MemoryPool < T > :: expandTheFreeList(int howMany)
{
	// We must allocate an object large enough to contain the
	// next pointer.
	static size_t size = (sizeof(T) > sizeof(Block *)) ? sizeof(T) : sizeof(Block *);
	Block *runner = (Block *)new char[size];//static_cast <MemoryPool<T> *> new char
	free_block = runner;
	for (int i = 0; i < howMany ; i++) 
	{
		runner->next =(Block *) new char [size]; //static_cast <Block *> new char [size];
			
		runner = runner->next;
	}
	runner->next = 0;
}


//#ifdef _UNICODE
//typedef wchar_t MyCharType;
//typedef std::wostream mystream;
//#else
//typedef char MyCharType;
//typedef std::basic_string<char> mystring;
//typedef std::ostream mystream;
//#endif

typedef std::basic_string<TCHAR> mystring;
typedef std::basic_ostream<TCHAR, std::char_traits<TCHAR>> mystream;
typedef std::basic_ofstream<TCHAR, std::char_traits<TCHAR>> myfile;

#define MSG_PRINT_LOG                       (WM_USER + 482)

class CLogging
{
public:
	enum MessageType
	{
		MessageQuit,
		MessageLog,
	};

	enum LoggingMode
	{
		LoggingMode_Polling,
		LoggingMode_ThreadMessage,
	};

	struct LogThreadMessage
	{
		LogThreadMessage()
		{
			type = MessageLog;	
		}

		void *operator new(size_t size) 
		{ 
			return memPool->alloc(size);
		}
		void operator delete(void *doomed,size_t size)
		{ 
			memPool->free(doomed); 
		}

		static void newMemPool() 
		{ 
			if (memPool == NULL)
				memPool = new MemoryPool <LogThreadMessage>; 
		}

		static void deleteMemPool() 
		{ 
			delete memPool; 
			memPool = NULL;
		}

		MessageType type;
		//std::string data;
		TCHAR data[256];

	private:
		static MemoryPool <LogThreadMessage>* memPool;
	};

public:
	CLogging(void);
	~CLogging(void);

	void init(mystring sFileDir,mystring sPrefixName,int mode);

	BOOL isLoggingViaThreadMessage() const
	{
		return m_nLoggingMode == LoggingMode_ThreadMessage;
	}

	void setFileCountLimit(int count)
	{
		m_nFileCountLimit = count;
	}

	void setFileSizeLimit(__int64 nFileSizeBytes)
	{
		m_nFileSizeLimit = nFileSizeBytes;
	}

	void setRotation(const std::string& rotation);

	__int64 getLogFileSize() const;


	void setLocale(const char* lang)
	{
		m_sLocale = lang;
	}

	bool CLogging::isRunning() const;

	HANDLE getThreadHandle()
	{
		return m_hLogThread;
	}

	void startupLogThread();

	static unsigned int WINAPI LogThreadFunc(void* lParam);

	//void sendLogMessage(char* message);
	void log(LPCTSTR pszFormat, ...);
	void stopLogThread();

private:
	unsigned int runAsPolling(mystream& logFile);
	unsigned int runAsMsgLoop(mystream& logFile);

	void postLogThreadMessage(LogThreadMessage* msg);

	bool existsFile(const mystring& path) const;
	void createFile(const mystring& path);
	void writeImpl(const mystring& text);

	__int64 getFileSize(const mystring& path) const;
	void rotateToNextFile();

private:
	CriticalSection					m_LogThreadQueueCS;
	std::vector<LogThreadMessage*>	m_LogThreadQueue;

	mystring						m_sLogFileDir;
	mystring						m_sLogPrefixName;
	mystring						m_sLogFilePath;
	std::string						_secondaryPath;
	std::string						_rotation;
	__int64							m_nFileSizeLimit;
	int								m_nFileCountLimit;

	myfile							m_logFile;
	//LogFile*						_pFile;
	__time64_t						m_tmCreationDate;
	__int64							m_nBytesWritten;

	std::string						m_sLocale;// use in std::locale() for language code page setting
	HANDLE							m_hLogThread;
	unsigned int					m_dwLogThreadId;
	ManualEvent						m_evLogEvent;

	int								m_nLoggingMode;
	long							m_count; // just for debugging
};


#endif